/*
 #
 #  Files       : BinaryTraits.h
 #                ( C++ header file )
 #
 #  Description : The SmallMatrix Library
 #                ( http://code.google.com/p/smallmatrix )
 #
 #  Copyright   : Olivier Juan
 #                ( http://www.mas.ecp.fr/vision/Personnel/juan/ )
 #
 #  License     : CeCILL-C
 #                ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
 #
 #  This software is governed by the CeCILL-C license under French law and
 #  abiding by the rules of distribution of free software.  You can  use,
 #  modify and or redistribute the software under the terms of the CeCILL-C
 #  license as circulated by CEA, CNRS and INRIA at the following URL
 #  "http://www.cecill.info".
 #
 #  As a counterpart to the access to the source code and  rights to copy,
 #  modify and redistribute granted by the license, users are provided only
 #  with a limited warranty  and the software's author,  the holder of the
 #  economic rights,  and the successive licensors  have only  limited
 #  liability.
 #
 #  In this respect, the user's attention is drawn to the risks associated
 #  with loading,  using,  modifying and/or developing or reproducing the
 #  software by the user in light of its specific status of free software,
 #  that may mean  that it is complicated to manipulate,  and  that  also
 #  therefore means  that it is reserved for developers  and  experienced
 #  professionals having in-depth computer knowledge. Users are therefore
 #  encouraged to load and test the software's suitability as regards their
 #  requirements in conditions enabling the security of their systems and/or
 #  data to be ensured and,  more generally, to use and operate it in the
 #  same conditions as regards security.
 #
 #  The fact that you are presently reading this means that you have had
 #  knowledge of the CeCILL-C license and that you accept its terms.
 #
*/

#ifndef _BINARYTRAITS_H
#define _BINARYTRAITS_H

#include <SmallMatrix/SmallMatrix.h>

NAMESPACE_BEGIN(SmallMatrix)

class Forbidden_Matrix_Product {};
class Forbidden_Matrix_Sum {};

template <class T1, class T2>
class Matrix_Product_Scheme_Trait {
private:
	template < class _T, bool >
	struct _Internal_Constraint {
		typedef _T result_type;
	};
	template < class _T >
	struct _Internal_Constraint< _T , false > {
		typedef Forbidden_Matrix_Product result_type;
	};
	template < class _T TPS_START_GCC(0) >
	struct is_not_forbidden_matrix_product {
		enum { Bool = true };
	};
	template < TPS_END_GCC(0) >
	struct is_not_forbidden_matrix_product < Forbidden_Matrix_Product TPS_GCC(0) > {
		enum { Bool = false };
	};
	template < class _T TPS_START_GCC(0)>
	struct is_reel{
		enum { Bool = false };
	};
	template < TPS_END_GCC(0) >
	struct is_reel< reel TPS_GCC(0) >{
		enum { Bool = true };
	};
	template < class _T1, class _T2 >
	struct _Internal {
		template < int _M, int __N TPS_START_GCC(0) >
		struct _Internal_Base {
			typedef Matrix< _M , __N > result_type;
		};
		template < int _M TPS_CONTINUE_GCC(0) >
		struct _Internal_Base < _M , _M TPS_GCC(0) > {
			typedef SquareMatrix< _M > result_type;
		};
		template < int _M TPS_CONTINUE_GCC(0) >
		struct _Internal_Base < _M , 1 TPS_GCC(0) > {
			typedef Vecteur< _M > result_type;
		};
		template < TPS_END_GCC(0) >
		struct _Internal_Base < 1 , 1 TPS_GCC(0) > {
			typedef reel result_type;
		};
		typedef typename _Internal_Base< _T1::ROW, _T2::COL >::result_type _to_verify_type;
		enum { Could_Multiply = (_T1::COL == _T2::ROW) };
		typedef typename _Internal_Constraint<_to_verify_type,Could_Multiply>::result_type result_type;
		enum { Scalar_Type = is_reel< result_type >::Bool };
	};
	template < class _T >
	struct _Internal < _T, _T >{
		template < int _M, int __N TPS_START_GCC(0) >
		struct _Internal_Base {
			typedef Forbidden_Matrix_Product result_type;
		};
		template < int _M TPS_CONTINUE_GCC(0) >
		struct _Internal_Base < _M , _M TPS_GCC(0) > {
			template < class __T TPS_START_GCC(1) >
			struct _Internal_Base_Base {
				typedef __T value_type;
			};
			template < TPS_END_GCC(1) >
			struct _Internal_Base_Base < Matrix< _M , _M > TPS_GCC(1) > {
				typedef SquareMatrix< _M > value_type;
			};
			template < TPS_END_GCC(1) >
			struct _Internal_Base_Base < SymMatrix< _M > TPS_GCC(1) > {
				typedef SquareMatrix< _M > value_type;
			};
			typedef typename _Internal_Base_Base<_T>::value_type result_type;
		};
		template < int _M TPS_CONTINUE_GCC(0) >
		struct _Internal_Base < _M , 1 TPS_GCC(0) > {
			typedef reel result_type;
		};
		template < TPS_END_GCC(0) >
		struct _Internal_Base < 1 , 1 TPS_GCC(0) > {
			typedef reel result_type;
		};
		typedef typename _Internal_Base< _T::ROW, _T::COL >::result_type result_type;
		enum { Could_Multiply = is_not_forbidden_matrix_product<result_type>::Bool };
		enum { Scalar_Type = is_reel< result_type >::Bool };
	};
	template < class _R, bool>
	struct _Matrix_Product {
		static INLINE _R compute(const T1& left, const T2& right) {
			_R result;
			matrix_product_Unrolled<_R::ROW>::template Operation<_R,T1,T2>::compute(result,left,right);
			return result;
		}
	};
	template < class _R >
	struct _Matrix_Product< _R , true > {
		static INLINE _R compute(const T1& left, const T2& right) {
			typedef Vecteur< T2::ROW > Vect;
			const Vect& Left = *reinterpret_cast<const Vect*>(&left);
			const Vect& Right = *reinterpret_cast<const Vect*>(&right);
			return Left.dot(Right);
		}
	};
public:
	typedef typename _Internal< T1, T2 >::result_type result_type;
	enum { Could_Multiply = _Internal< T1, T2 >::Could_Multiply };
	enum { Scalar_Type = _Internal< T1, T2 >::Scalar_Type };
	typedef _Matrix_Product< result_type, Scalar_Type > Product;
};

//template <class T>
//class Matrix_Product_Scheme_Trait< T, T > {
//private:
//	template < class _T >
//	struct is_not_forbidden_matrix_product {
//		enum { Bool = true };
//	};
//	template < >
//	struct is_not_forbidden_matrix_product < Forbidden_Matrix_Product > {
//		enum { Bool = false };
//	};
//	template < class _T >
//	struct is_reel{
//		enum { Bool = false };
//	};
//	template < >
//	struct is_reel< reel >{
//		enum { Bool = true };
//	};
//	template <int M, int N>
//	struct _Internal{
//		template <int _M,int _N>
//		struct _Internal_Base {
//			typedef Forbidden_Matrix_Product result_type;
//		};
//		template <int _M>
//		struct _Internal_Base < _M , _M > {
//			template <class _T>
//			struct _Internal_Base_Base {
//				typedef _T value_type;
//			};
//			template <>
//			struct _Internal_Base_Base < Matrix_Base < Matrix< _M , _M > > > {
//				typedef SquareMatrix< _M > value_type;
//			};
//			typedef typename _Internal_Base_Base<T>::result_type result_type;
//		};
//		template <int _M>
//		struct _Internal_Base < _M , 1 > {
//			typedef reel result_type;
//		};
//		template <>
//		struct _Internal_Base < 1 , 1 > {
//			typedef reel result_type;
//		};
//		typedef typename _Internal_Base<M,N>::result_type result_type;
//		enum { Could_Multiply = is_not_forbidden_matrix_product<result_type>::Bool };
//		enum { Scalar_Type = is_reel< result_type >::Bool };
//	};
//	template < class _R, bool>
//	struct _Matrix_Product {
//		static INLINE _R compute(const T& left, const T& right) {
//			return _R().prod(left,right);
//		}
//	};
//	template < class _R >
//	struct _Matrix_Product< _R, true > {
//		static INLINE _R compute(const T& left, const T& right) {
//			typedef Vecteur<T::ROW> Vect;
//			return reinterpret_cast<const Vect*>(&left)->dot(*reinterpret_cast<const Vect*>(&right));
//		}
//	};
//
//public:
//	typedef typename _Internal<T::ROW, T::COL>::result_type result_type;
//	enum { Could_Multiply = _Internal<T::ROW, T::COL>::Could_Multiply };
//	enum { Scalar_Type = _Internal<T::ROW, T::COL>::Scalar_Type };
//	typedef _Matrix_Product< result_type, Scalar_Type > Product;
//};

template <class T1, class T2>
class Matrix_Additive_Scheme_Trait {
private:
	template < class _T, bool>
	struct _Internal_Constraint {
		typedef _T result_type;
	};
	template < class _T >
	struct _Internal_Constraint< _T , false > {
		typedef Forbidden_Matrix_Sum result_type;
	};
	template < class _T TPS_START_GCC(0) >
	struct is_reel{
		enum { Bool = false };
	};
	template < TPS_END_GCC(0) >
	struct is_reel< reel TPS_GCC(0) >{
		enum { Bool = true };
	};
	template < class _T1, class _T2 >
	struct _Internal {
		template < int _M, int __N TPS_START_GCC(0) >
		struct _Internal_Base {
			typedef Matrix< _M , __N > result_type;
		};
		template < int _M TPS_CONTINUE_GCC(0) >
		struct _Internal_Base < _M , _M TPS_GCC(0) > {
			typedef SquareMatrix< _M > result_type;
		};
		template < int _M TPS_CONTINUE_GCC(0) >
		struct _Internal_Base < _M , 1 TPS_GCC(0) > {
			typedef Vecteur< _M > result_type;
		};
		template < TPS_END_GCC(0) >
		struct _Internal_Base < 1 , 1 TPS_GCC(0) > {
			typedef reel result_type;
		};
		typedef typename _Internal_Base< _T1::ROW, _T2::COL >::result_type _to_verify_type;
		enum { Could_Add = (_T1::COL == _T2::COL) && (_T1::ROW == _T2::ROW) };
		typedef typename _Internal_Constraint< _to_verify_type , Could_Add >::result_type result_type;
		enum { Scalar_Type = is_reel< result_type >::Bool };
	};
	template < int M >
	struct _Internal < SymMatrix < M >, SymMatrix < M > > {
		template < int _M TPS_START_GCC(0) >
		struct _Internal_Base {
			typedef SymMatrix< _M > result_type;
		};
		template < TPS_END_GCC(0) >
		struct _Internal_Base < 1 TPS_GCC(0) > {
			typedef reel result_type;
		};
		typedef typename _Internal_Base< M >::result_type result_type;
		enum { Could_Add = true };
		enum { Scalar_Type = is_reel< result_type >::Bool };
	};
	template < class _R, bool>
	struct _Matrix_Sum {
		static INLINE _R compute(const T1& left, const T2& right) {
			_R temp(left);
			operation_Unrolled<_R::ROW>::template Operation<_R,T2,Op_Plus>::compute(temp,right.Child());
			return temp;
		}
	};
	template < class _R >
	struct _Matrix_Sum< _R , true > {
		static INLINE _R compute(const T1& left, const T2& right) {
			return left.template Access<0,0>()+right.template Access<0,0>();
		}
	};
	template < class _R, bool>
	struct _Matrix_Diff {
		static INLINE _R compute(const T1& left, const T2& right) {
			_R temp(left);
			operation_Unrolled<_R::ROW>::template Operation<_R,T2,Op_Minus>::compute(temp,right.Child());
			return temp;
		}
	};
	template < class _R >
	struct _Matrix_Diff< _R , true > {
		static INLINE _R compute(const T1& left, const T2& right) {
			return left.template Access<0,0>()-right.template Access<0,0>();
		}
	};
public:
	typedef typename _Internal< T1, T2 >::result_type result_type;
	enum { Could_Add = _Internal< T1, T2 >::Could_Add };
	enum { Scalar_Type = _Internal< T1, T2 >::Scalar_Type };
	typedef _Matrix_Sum< result_type, Scalar_Type > Sum;
	typedef _Matrix_Sum< result_type, Scalar_Type > Diff;
};

//template <int M>
//class Matrix_Additive_Scheme_Trait < Matrix_Base< SymMatrix < M > > , Matrix_Base< SymMatrix < M > > > {
//private:
//
//	template <int _M>
//	struct _Internal {
//		typedef SymMatrix< _M > result_type;
//	};
//	template <>
//	struct _Internal < 1 > {
//		typedef reel result_type;
//	};
//
//	template < class _T >
//	struct _Internal_Temp {
//		typedef _T temp_type;
//	};
//	template <>
//	struct _Internal_Temp < reel > {
//		typedef Vecteur< 1 > temp_type;
//	};
//public:
//	typedef typename _Internal< M >::result_type result_type;
//	typedef typename _Internal_Temp<result_type>::temp_type temp_type;
//};
//
//template <class T1,class T2>
//class Matrix_Strictly_Additive_Scheme_Trait {
//	typedef typename Matrix_Additive_Scheme_Trait< T1 , T2 >::result_type T3;
//	STATIC_EGALITY_ASSERT( T1 , T3 );
//public:
//	typedef T1 result_type;
//};
template <class T1,class T2>
class Matrix_Convert_Trait {
	template < class _T1, class _T2 >
	struct _Internal {
		template < class __T1, class __T2 >
		struct _Internal_Base {
			enum { Could_Convert = true };
		};
		template < int _M, class __T2 >
		struct _Internal_Base< SymMatrix<_M>, __T2 > {
			enum { Could_Convert = false };
		};
		enum { Dim_Check = (_T1::ROW==_T2::ROW) && (_T1::COL==_T2::COL) };
		enum { Could_Convert_Bis = Dim_Check ? _Internal_Base<_T1,_T2>::Could_Convert : false };
		enum { Dim_1 = (_T1::ROW==1) && (_T1::COL==1) };
		enum { Could_Convert = Dim_1 ? true : Could_Convert_Bis };
	};
	template <class _T>
	struct _Internal<_T,_T> {
		enum { Could_Convert = true };
	};
	template <class _T1>
	struct _Internal< _T1, reel > {
		template < int _M, int __N TPS_START_GCC(0)  >
		struct _Internal_Base {
			enum { Could_Convert = false };
		};
		template < TPS_END_GCC(0) >
		struct _Internal_Base< 1, 1 TPS_GCC(0)> {
			enum { Could_Convert = true };
		};
		enum { Could_Convert = _Internal_Base<_T1::ROW,_T1::COL>::Could_Convert };
	};
public:
	enum { Could_Convert = _Internal<T1,T2>::Could_Convert };
};


NAMESPACE_END
#endif
